package file

import (
	"github.com/gofrs/flock"
	"github.com/saintfish/chardet"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

// 将文件路径转换为系统路径分隔符
func ConvertPathSeparator(filePath string) string {
	filePath = strings.ReplaceAll(filePath, "/", string(os.PathSeparator))
	filePath = strings.ReplaceAll(filePath, "\\", string(os.PathSeparator))
	return filePath
}

// 路径拼接
func JoinPath(path ...string) string {
	var result string
	for _, p := range path {
		result = filepath.Join(result, p)
	}
	return ConvertPathSeparator(result)
}

// 删除文件 ,如果文件不存在,则抛出异常
func DeleteFile(filePath string) {
	err := os.Remove(filePath)
	if err != nil {
		panic(err)
	}
}

// 判断文件是否存在
func IsExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}

// 判断文件的上级目录是否存在
func IsDirExist(path string) bool {
	dir := filepath.Dir(path)
	return IsExist(dir)
}

// 判断文件上级目录是否存在,不存在则创建
func IsDirExistAndCreate(path string) {
	dir := filepath.Dir(path)
	if !IsExist(dir) {
		CreateDir(dir)
	}
}

// 创建文件
func CreateFile(filePath string) {
	file, err := os.Create(filePath)
	if err != nil {
		panic(err)
	}
	defer file.Close()
}

// 创建文件夹
func CreateDir(dirPath string) {
	err := os.MkdirAll(dirPath, os.ModePerm)
	if err != nil {
		panic(err)
	}
}

/**
 * @brief:  批量移动文件
 * @param:  files: 文件列表
 * @param:  dest: 目标文件夹
 * @detail: 比如: MoveFiles([]string{"a.txt", "b.txt"}, "a/")
 */
func MoveFiles(files []string, destDir string) {
	dest, err := os.Stat(destDir)
	if err != nil {
		//如果目标文件夹不存在，则创建
		if os.IsNotExist(err) {
			err := os.MkdirAll(destDir, 0775)
			if err != nil {
				panic(err)
			}
			dest, err = os.Stat(destDir)
		} else {
			panic(err)
		}
	}
	if err != nil {
		panic(err)
	}
	if dest.IsDir() { // 是文件夹
		for _, file := range files {
			//获取最后的路径分隔符下标
			index := strings.LastIndex(file, string(os.PathSeparator))
			//获取文件夹名称
			dirName := file[index+1:]
			err := os.Rename(file, filepath.Join(destDir, dirName))
			if err != nil {
				panic(err)
			}
		}
	}

}

// 获取文件编码
func GetFileEncoding(filePath string) string {
	// 读取文件内容
	data, err := os.ReadFile(filePath)
	if err != nil {
		panic(err)
	}
	// 检测文件编码格式
	det := chardet.NewTextDetector()
	result, err := det.DetectBest(data)
	if err != nil {
		panic(err)
	}
	return result.Charset
}

/**
 * @brief:  批量复制文件
 * @param:  files: 文件列表
 * @param:  dest: 目标文件夹
 * @detail: 比如: CopyFiles([]string{"a.txt", "b.txt"}, "a")
 */
func CopyFiles(files []string, destDir string) {
	dest, err := os.Stat(destDir)
	if err != nil {
		//如果目标文件夹不存在，则创建
		if os.IsNotExist(err) {
			err := os.MkdirAll(destDir, 0775)
			if err != nil {
				panic(err)
			}
			dest, err = os.Stat(destDir)
		} else {
			panic(err)
		}
	}
	if dest.IsDir() { // 是文件夹
		for _, file := range files {
			oldFile, err := os.Open(file)
			if err != nil {
				panic(err)
			}
			defer oldFile.Close()
			//判断是否是文件夹
			fileInfo, err := oldFile.Stat()
			if fileInfo.IsDir() {
				var files1 []string
				GetFiles(file, &files1)
				//获取最后的路径分隔符下标
				index := strings.LastIndex(file, string(os.PathSeparator))
				//获取文件夹名称
				dirName := file[index+1:]
				CopyFiles(files1, destDir+string(os.PathSeparator)+dirName)
				return
			}
			fileName := filepath.Base(file)
			destFile, _ := os.OpenFile(filepath.Join(destDir, fileName), os.O_RDWR|os.O_CREATE, 0775)
			defer destFile.Close()
			// 读写取文件内容
			buf := make([]byte, 2048)
			for {
				n, err := oldFile.Read(buf)
				if err == io.EOF {
					break
				}
				destFile.Write(buf[:n])
			}
		}
	}
}

/**
 * @brief:  获取指定文件夹下的所有文件,包含子文件夹
 * @param:  dir: 文件夹路径
 * @param:  files: 文件列表
 * @return: []string: 文件列表
 *@detail: 比如: GetFiles("a/")
 */
func GetFiles(dir string, files *[]string) {

	file, err := os.Open(dir)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	fileInfos, err := file.Readdir(-1)
	if err != nil {
		panic(err)
	}
	for _, fileInfo := range fileInfos {
		if fileInfo.IsDir() {
			*files = append(*files, filepath.Join(dir, fileInfo.Name()))
			GetFiles(dir+string(os.PathSeparator)+fileInfo.Name(), files)
		} else {
			*files = append(*files, filepath.Join(dir, fileInfo.Name()))
		}
	}
}

/**
 * @brief:  获取指定文件夹下的所有文件,不保护子文件夹
 * @param:  dir: 文件夹路径
 * @param:  files: 文件列表
 * @return: []string: 文件列表
 *@detail: 比如: GetFile("a/")
 */
func GetFile(dir string, files *[]string) {
	file, err := os.Open(dir)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	fileInfos, err := file.Readdir(-1)
	if err != nil {
		panic(err)
	}
	for _, fileInfo := range fileInfos {
		if fileInfo.IsDir() {
			*files = append(*files, filepath.Join(dir, fileInfo.Name()))
			continue
		} else {
			*files = append(*files, filepath.Join(dir, fileInfo.Name()))
		}
	}
}

/**
 * @brief:  获取当前项目根目录
 * @param:  projectName: 项目名称
 * @return: string: 项目根目录
 * @detail: 比如: GetCurrentDirectory("go-utils") -> F:\go-project\go-utils
 */
func GetCurrentDirectory(projectName string) string {
	dir, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	//正则表达式截取项目根目录
	reg := regexp.MustCompile(`(.*)` + projectName)
	return filepath.Join(reg.FindStringSubmatch(dir)[1], projectName)
}

/**
 * @brief:  搜索指定文件夹下的满足条件的文件
 * @param:  dir: 文件夹路径
 * @param:  files: 文件列表
 * @param:  reg: 正则表达式
 */
func SearchFiles(dir string, files *[]string, reg *regexp.Regexp) {

	file, err := os.Open(dir)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	fileInfos, err := file.Readdir(-1)
	if err != nil {
		panic(err)
	}
	for _, fileInfo := range fileInfos {
		if fileInfo.IsDir() {
			SearchFiles(filepath.Join(dir, fileInfo.Name()), files, reg)
		} else {
			if reg.MatchString(fileInfo.Name()) {
				*files = append(*files, filepath.Join(dir, fileInfo.Name()))
			}
		}
	}
}

// 获取文件的后缀 ,比如: GetFileSuffix("a.txt") -> .txt
func GetFileSuffix(fileName string) string {
	index := strings.LastIndex(fileName, ".")
	if index == -1 {
		return ""
	}
	return fileName[index:]
}

// 获取文件的目录
func GetFileDir(fileName string) string {
	index := strings.LastIndex(fileName, string(os.PathSeparator))
	return fileName[:index]
}

// 获取用户目录
func GetUserHomeDir() string {
	dir, err := os.UserHomeDir()
	if err != nil {
		panic(err)
	}
	return dir
}

// 文件锁, 当文件被锁定时,返回false,否则返回true
func FileLock(lockfileName string, fun func()) bool {
	//获取缓存目录
	cacheDir := filepath.Join(os.TempDir(), "gp_file_lock")
	//判断缓存目录是否存在
	if !IsExist(cacheDir) {
		//创建缓存目录
		err := os.MkdirAll(cacheDir, os.ModePerm)
		if err != nil {
			panic(err)
		}
	}
	//创建锁文件
	lockFilePath := filepath.Join(cacheDir, lockfileName)
	fileLock := flock.New(lockFilePath)
	//尝试加锁
	locked, err := fileLock.TryLock()
	if err != nil {
		panic(err)
	}
	if !locked {
		return false
	}
	defer fileLock.Unlock()
	fun()
	return true
}

// 判断文件内容是否为空
func IsFileEmpty(filePath string) bool {
	file, err := os.Open(filePath)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	fileInfo, err := file.Stat()
	if err != nil {
		panic(err)
	}
	return fileInfo.Size() == 0
}
